
<!--Markdown
# Visualizer exemplar

This is the pattern used to write the visualizers like CayleyDiagram and Multtable. The entire code of example is contained below, with comments interspersed.

## Visualizer invocation

The visualizer exemplar may be viewed in the browser by entering a URL like
     <br>&nbsp;&nbsp;&nbsp;&nbsp;
     [http://.../group-explorer/docs/visualizerExemplar.html?groupURL=../groups/D_4.group](
     ./visualizerExemplar.html?groupURL=../groups/D_4.group),
<br>which passes the visualizer the URL of a group definition in .group file XML, or
     <br>&nbsp;&nbsp;&nbsp;&nbsp;
     [http://.../group-explorer/docs/visualizerExemplar.html?groupJSON=../groups/D_4.json](
     ./visualizerExemplar.html?groupJSON=../groups/D_4.json),
<br>which passes the visualizer the URL of a group definition in JSON format. (Note that this won't work with the current release -- there are no .json files in the library.)

In the normal use of GE3 the visualizers are opened from the GroupInfo page by selecting one of the visualizer thumbnails. The GroupInfo page opens the visualizer with a URL of the form
     <br>&nbsp;&nbsp;&nbsp;&nbsp;
     [http://.../group-explorer/Multtable.html?groupURL=groups/D_4.group](
     ../Multtable.html?groupURL=groups/D_4.group),
<br>passing it a URL referencing a group definition in XML (.group) format. Other parameters may also be passed in the URL.
For example, this invocation of the Cayley diagram visualizer
     <br>&nbsp;&nbsp;&nbsp;&nbsp;
     [http://.../group-explorer/CayleyDiagram.html?groupURL=groups/S_4.group&diagram=Truncated%20cube](
     ../CayleyDiagram.html?groupURL=groups/S_4.group&diagram=Truncated%20cube)
<br>initially displays the `Truncated cube` diagram of the S<sub>4</sub> group.

## Visualizer display

The visualizer exemplar displays
- a formatted header with the name of the group passed in the URL
- a blank graphic element
- a functional splitter element that can be used to resize the graphic and the controls panel
- a functional subgroup control panel, common to several of the visualizers
- a non-functional view control panel, with examples of a select element and a couple of sliders
- buttons to choose between viewing the subgroup control panel and the view control panel

Other visualizers extend the exemplar with different displays in the graphic element and specialized control panels that interact with the display.

```html
Markdown-->
<html>
   <head>
      <meta charset="utf-8" />
      <base href=".."></base>

      <link rel="icon" type="image/png" href="./images/favicon.png"></link>
      <link rel="stylesheet" href="./style/fonts.css" type="text/css"></link>
      <link rel="stylesheet" href="./style/sliders.css" type="text/css"></link>
      <link rel="stylesheet" href="./visualizerFramework/visualizer.css" type="text/css"></link>
      <link rel="stylesheet" href="./style/menu.css" type="text/css"></link>
      <link rel="stylesheet" href="./style/SubsetHighlightController.css" type="text/css"></link>
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/css/font-awesome.min.css">

      <style>
       /* Container for visualizer-specific controls */
       #graphic {
           background-color: #e8c8c8;  /*: Cayley diagram background color */
       }

       /* buttons for choosing control panel */
       #control {
           padding: 2px;
           overflow: auto;
           min-width: 250px;
       }
       #subset-control {
           padding: 2px;
           overflow: auto;
       }
       #view-control {
       }
      </style>

      <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<!--Markdown
```
### Visualizer Javascript

(The following code would generally be found in a separate .js file, not in the .html file.)
```javascript
Markdown-->
      <script type="module">
       import GEUtils from './js/GEUtils.js';
       import * as Library from './js/Library.js';
       import Log from './js/Log.js';
       import * as MathML from './js/MathML.js';
       import Template from './js/Template.js';
       import * as VC from './visualizerFramework/visualizer.js';
       import * as SSD from './js/SubsetHighlightController.js';
       
       /* Global variables */
       var Group;  // group about which information will be displayed
       const HELP_PAGE = 'help/index.html';

       /* Initial entry to javascript -- called once after document load */
       $(window).one('load', load);

       /*
        * Register static event managers -- called after document is assembled
        *    (The .off(--).on(--) sequence is used to avoid accumulating event handlers after a reset.)
        */
       function registerEventHandlers() {
           window.addEventListener('resize', resizeBody);
           $('#bodyDouble')[0].addEventListener('click', GEUtils.cleanWindow);
           $('#subgroup-select')[0].addEventListener('click', actionClickHandler);
       }
/*
```
### Asynchronous loading

Asynchronous tasks are generally presented as ES6 Promises in GE. Since the [Library](../js/Library.js) will request the group definition from a server if it doesn't have a copy locally, the [loadFromURL()](../js/Library.js#loadFromURL) method returns a Promise, not the group itself.
```javascript
*/
       /* Load the static components of the page */
       async function load () {
           // Check for groupURL
           if (new URL(window.location.href).searchParams.get('groupURL') == undefined) {
               alert('no group specified in URL, unable to proceed');
               return;
           }

           // Load group from invocation URL
           const Group = await Library.loadFromURL()

           const dummy_highlighters = [
               {handler: () => alert('Dummy highlighter #1'), label: 'Dummy highlighter #1'},
               {handler: () => alert('Dummy highlighter #2'), label: 'Dummy highlighter #2'},
               {handler: () => alert('Dummy highlighter #3'), label: 'Dummy highlighter #3'}
           ];
           const dummy_highlight_clearer = () => alert('Dummy highlight clearer');
           SSD.load($('#subset-control'), dummy_highlighters, dummy_highlight_clearer, Group);
           
           // Document is assembled, register event handlers
           registerEventHandlers();

           // Create header from group name
           $('#heading').html(`Visualizer Example for ${Group.name}`);

           // Populate 'faux-select' pulldown
           $('#subgroup-choices')
               .append( 
                   [...Array(Group.subgroups.length - 1).keys()]
                       .reduce( ($frag, index) => {
                           if (index == 0) {
                               $frag.append(eval(Template.HTML('subgroup-choice-none-template')));
                           } else {
                               const subgroupIndex = index;
                               const subgroup = Group.subgroups[subgroupIndex];
                               $frag.append(eval(Template.HTML('subgroup-choice-template')));
                           }
                           return $frag;
                       }, $(document.createDocumentFragment()) ))
               .css('visibility', 'hidden');
           choose(0);

           // Load icon strip in upper right-hand corner
           VC.load(Group, HELP_PAGE);
       }

       // Resize body (including graphic, if necessary).
       function resizeBody() {
           $('body').height(window.innerHeight);
           $('body').width(window.innerWidth);

           // resize the visualizer graphic here
       }

       function toggleSubgroupChoices () {
           const choices = $('#subgroup-choices');
           const new_visibility = choices.css('visibility') == 'visible' ? 'hidden' : 'visible';
           choices.css('visibility', new_visibility);
       }

       function actionClickHandler (event /*: MouseEvent */) {
           event.preventDefault();
           const $action = $(event.target).closest('[action]');
           if ($action.length != 0) {
               event.stopPropagation();
               eval($action.attr('action'));
           }
       }
       
       // Do-nothing event handler, set up in registerCallback
       function choose (index) {
           $('#subgroup-choice').html($(`#subgroup-choices > li:nth-of-type(${index+1})`).html());
           GEUtils.cleanWindow();
       }
      </script>
   </head>
<!--Markdown
```
## HTML

The HTML lays out the visualizer elements in the [format](./visualizerLayout.md) used by GE3.

The string of icons in the upper right-hand corner of the screen is placed there by the [VC.load(group, HELP_PAGE)](../visualizerFramework/visualizer.md#load) call during [initialization](#exemplar-initialization). It is defined in [visualizerFramework.html](./visualizerFramework_html.md).

Note the use of [faux-select](../visualizerFramework/visualizer_css.md#faux-select) and associated classes to simulate an HTML select element with HTML formatted text.
```html
Markdown-->
   <body>
       <div id="bodyDouble">
           <div id="header">
               <div id="heading"></div>
           </div>
           <div id="graphic">
               <div id="controls">
                   <div id="options">
                       <button id="subset-button"
                               onclick="$('#control > *').hide(); $('#subset-control').show()">Subsets</button>
                       <button id="view-button"
                               onclick="$('#control > *').hide(); $('#view-control').show()">View</button>
            </div>

            <div id="control">
                <div id="subset-control" class="panel">
                    <!-- This is filled in from html/SubsetHighlightController.html -->
                </div>

                <div id="view-control" class="panel hidden">
                    <p>Choose subgroup:</p>
                    <div id="subgroup-select" class="faux-select" action="toggleSubgroupChoices()">
                        <div style="display: inline-block; line-height: 1.5em">&nbsp;</div>
                        <div id="subgroup-choice" class="faux-selection"></div>
                        <div class="faux-select-arrow" ></div>
                        <ul id="subgroup-choices" class="faux-select-options visibility-hidden-on-clean"></ul>
                    </div>
                    <template id="subgroup-choice-none-template">
                        <li action="choose(${index})" style="margin-right: 1em">none</li>
                    </template>
                    <template id="subgroup-choice-template">
                        <li action="choose(${index})" style="margin-right: 1em">
                            <i>H</i><sub>${subgroupIndex}</sub>, a subgroup of order ${subgroup.order}
                        </li>
                    </template>

                    <p>Zoom level:</p>
                    <input id="zoom-level" type="range" min="-10" max="10" value="0">

                    <p>Line thickness:</p>
                    <input id="line-thickness" type="range" min="1" max="20" value="10">
                </div>
            </div>
         </div>
         </div>
      </div>
   </body>
</html>
<!--Markdown
```
Markdown-->
